(function (){
    
    function mergeOptions(o,d){for(k in d){o[k]=o.hasOwnProperty(k)?o[k]:d[k];} return o;}

    jCSV = function (onComplete,onData){ 
        if ( !(this instanceof jCSV) ) { return new jCSV(); }
    }

    jCSV.init = function (){return {
        SE : /"(""|[^"])*"/g,
        SN : /[^,\r\n]+/g,
        DQDQ:/""/g,
        data : [],
        record : [],
        pos:0,
        state:0
        };}

    var nextToken = function (ds){
        var r = null;
        if(ds.pos < ds.buffer.length){
            switch(ds.buffer.charAt(ds.pos)){
                case ',' :
                case '\r':
                case '\n':
                        r = [ds.buffer.charAt(ds.pos)];
                        r.index = ds.pos;
                    break;
                case '"' :
                        ds.SE.lastIndex = ds.pos;
                        r = ds.SE.exec(ds.buffer);
                    break;
                default  :
                        ds.SN.lastIndex = ds.pos;
                        r = ds.SN.exec(ds.buffer);
                    break;
            }
        }
        return r;
    }

    jCSV.tokenize = function(ds,buffer){
        if(ds.buffer){
            var reminder = (ds.pos<ds.buffer.length)?ds.buffer.slice(ds.pos,ds.buffer.length):"";
            if(ds.tokens.length>0 && (2 == ds.state || 3 == ds.state || 7 == ds.state)){
                reminder = ds.tokens.slice(ds.idx-1,ds.tokens.length).join("") + reminder;
                ds.state = ds.prev;
            }
            ds.buffer = reminder + buffer;
            ds.pos = 0;
        }else{
            ds.buffer = buffer;
        }
        ds.tokens = [];
        var r = nextToken(ds);
        while( null != r && ds.pos == r.index ){
            ds.tokens.push(r[0]);
            ds.pos += r[0].length;
            r = nextToken(ds);
        }
        return ds;
    }

    //states: ST CM NE ES CR LF FN XX
    //        0  1  2  3  4  5  6  7
    jCSV.processTokens = function(ds){
        for(var idx=0; idx < ds.tokens.length && 7 != ds.state; idx++){
            switch(ds.tokens[idx].charAt(0)){
                case ',' : 
                    switch(ds.state){
                        case 0: case 4: case 5: 
                        case 1: ds.record.push(""); break;
                        case 2: ds.record.push(ds.tokens[idx-1]); break;
                        case 3: ds.record.push(ds.tokens[idx-1].slice(1,-1).replace(ds.DQDQ,"\"")); break;
                    }
                    ds.prev = ds.state; ds.state =1; 
                    break;
                case '\"':
                    switch(ds.state){
                        case 0: case 1: case 4: case 5: ds.prev = ds.state; ds.state =3; break;
                        case 2: case 3: ds.prev = ds.state; ds.state =7; break;
                    }
                    break;
                case '\r':
                    switch(ds.state){
                        case 0: case 4: case 5: 
                        case 1: 
                            ds.record.push(""); 
                            ds.data.push(ds.record); ds.record = [];
                        break;
                        case 2: 
                            ds.record.push(ds.tokens[idx-1]); 
                            ds.data.push(ds.record); ds.record = [];
                        break;
                        case 3: 
                            ds.record.push(ds.tokens[idx-1].slice(1,-1).replace(ds.DQDQ,"\"")); 
                            ds.data.push(ds.record); ds.record = [];
                        break;
                    }
                    ds.prev = ds.state; ds.state =4;
                    break;
                case '\n':
                    switch(ds.state){
                        case 4: break; 
                        case 0: case 5: 
                            ds.record.push(""); 
                            ds.data.push(ds.record); ds.record = [];
                        break;
                        case 2: 
                            ds.record.push(ds.tokens[idx-1]); 
                            ds.data.push(ds.record); ds.record = [];
                        break;
                        case 3: 
                            ds.record.push(ds.tokens[idx-1].slice(1,-1).replace(ds.DQDQ,"\"")); 
                            ds.data.push(ds.record); ds.record = [];
                        break;
                    }
                    ds.prev = ds.state; ds.state =5;
                    break;
                default  :
                    switch(ds.state){
                        case 0: case 1: case 4: case 5: ds.prev = ds.state; ds.state =2; break;
                        case 2: case 3: ds.prev = ds.state; ds.state =7; break;
                    }
                    break;
            }
        }
        ds.idx = idx;
    }
    
    jCSV.closeBuffer = function(ds){
        switch(ds.state){
            case 4: case 5: sate = 6;break; 
            case 0: case 1:  
                ds.record.push(""); 
                ds.data.push(ds.record); ds.record = [];
                state = 6;
            break;
            case 2: 
                ds.record.push(ds.tokens[ds.idx-1]); 
                ds.data.push(ds.record); ds.record = [];
                state = 6;
            break;
            case 3: 
                ds.record.push(ds.tokens[ds.idx-1].slice(1,-1).replace(ds.DQDQ,"\"")); 
                ds.data.push(ds.record); ds.record = [];
                state = 6;
            break;
        }
    }

    jCSV.parse = function(){}

    jCSV.toArrays = function(buffer,options){
        var defaults = {max:1024*100};
        var o = mergeOptions(options||{},defaults);
        var ds = jCSV.init(); 
        for(var i=0 ; i < buffer.length; i+=o.max){
            jCSV.tokenize(ds,buffer.slice(i,(i+o.max < buffer.length)?i+o.max:buffer.length));
            jCSV.processTokens(ds);
        }
        jCSV.closeBuffer(ds);
        return ds;
    }

    jCSV.prototype.write = function(buffer){
    }

    jCSV.prototype.writeAll = function(buffer){
    }

    jCSV.prototype.close = function(){
    }

    if('object' == typeof exports){
        module.exports = jCSV;
    }

})();

